﻿using CocoaAni.Net.WebSockets.Exceptions;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;

namespace CocoaAni.Net.WebSockets;

public class WebSocketListener
{
    public Uri Url { get; init; }
    public int Backlog { get; init; }
    public Socket? Socket { get; protected set; }
    protected Func<Stream, Task<SslStream>>? SecurityStreamInitializer { get; set; }

    public WebSocketListener(string url, int backlog, Func<Stream, Task<SslStream>>? sslInitializer = null)
    {
        Url = new Uri(url);
        Backlog = backlog;
        SecurityStreamInitializer = sslInitializer;
        if (Url.Scheme == "wss://" && sslInitializer == null)
        {
            throw new ArgumentNullException("Use wss:// Please Set =>" + nameof(sslInitializer));
        }
    }

    public WebSocketListener(Uri uri, int backlog, Func<Stream, Task<SslStream>>? sslInitializer = null)
    {
        Url = uri;
        Backlog = backlog;
        SecurityStreamInitializer = sslInitializer;
        if (Url.Scheme == "wss://" && sslInitializer == null)
        {
            throw new ArgumentNullException("Use wss:// Please Set =>" + nameof(sslInitializer));
        }
    }

    public async Task InitAsync(CancellationToken cancellationToken = default)
    {
        if (Socket != null) return;
        var ipAddresses = await Dns.GetHostAddressesAsync(Url.Host, cancellationToken);
        if (ipAddresses.Length == 0)
            throw new WebSocketListenerException($"不知道这样的主机地址：{Url.Host}");
        Socket = new Socket(ipAddresses[0].AddressFamily, SocketType.Stream, ProtocolType.IP);
        Socket.Bind(new IPEndPoint(ipAddresses[0], Url.Port));
        Socket.Listen(Backlog);
    }

    public async Task<Task<WebSocket>> AcceptAsync(CancellationToken cancellationToken = default)
    {
        if (Socket == null)
        {
            throw new WebSocketListenerException("Please Invoked The InitAsync Before This Method");
        }
        var clientSocket = await Socket.AcceptAsync(cancellationToken);
        return CreateWebSocketAsync(clientSocket, cancellationToken);
    }

    public async Task<WebSocket> CreateWebSocketAsync(Socket socket, CancellationToken cancellationToken = default)
    {
        var client = new WebSocket(socket);
        await client.InitForServerSideAsync(null, null, cancellationToken);
        return client;
    }
}